package com.zhixing.myboot.framework.utils;


import javax.crypto.Cipher;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;

/**
 * 基于PKCS8的RSA工具集，此方法无法直接用于Open SSL生成的key pair
 * 可使用命令行工具相互转换
 */
public class RSAUtils {


    public static final String PUBLIC_KEY_NAME = "publicKey";

    public static final String PRIVATE_KEY_NAME = "privateKey";

    /**
     * 随机生成Base64编码的RSA密钥组
     *
     * @param keysize RSA密钥长度，单次可加密的密文长度是 keysize/8 - 11 byte,例如1024单次可加密长度为117byte的密文
     *                <br>过短安全性得不到保证，过长加密解密时间太久，以当前的硬件水平建议选择1024
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @see RSAUtils#PUBLIC_KEY_NAME 返回的公钥的key名
     * @see RSAUtils#PRIVATE_KEY_NAME 返回的私钥的key名
     */
    public static HashMap<String, String> genRandomKeyPair(int keysize) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(keysize);//生成大小 1024
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();//获取公钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();//获取私钥
        HashMap<String, String> keyMap = new HashMap<>();
        keyMap.put(PUBLIC_KEY_NAME, Base64.getEncoder().encodeToString(publicKey.getEncoded()));//获取公钥Base64编码
        keyMap.put(PRIVATE_KEY_NAME, Base64.getEncoder().encodeToString(privateKey.getEncoded()));//获取密钥Base64编码
        return keyMap;
    }

    /**
     * 通过字符串生成私钥
     */
    public static PrivateKey getPrivateKey(String privateKeyData) {
        PrivateKey privateKey = null;
        try {
            byte[] decodeKey = Base64.getDecoder().decode(privateKeyData);   //将字符串Base64解码
            PKCS8EncodedKeySpec x509 = new PKCS8EncodedKeySpec(decodeKey);   //创建x509证书封装类
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");           //指定RSA
            privateKey = keyFactory.generatePrivate(x509);                    //生成私钥
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return privateKey;
    }


    /**
     * 通过字符串生成公钥
     */
    public static PublicKey getPublicKey(String publicKeyData) {
        PublicKey publicKey = null;
        try {
            byte[] decodeKey = Base64.getDecoder().decode(publicKeyData);
            X509EncodedKeySpec x509 = new X509EncodedKeySpec(decodeKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            publicKey = keyFactory.generatePublic(x509);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return publicKey;
    }

    /**
     * 加密
     */
    public static byte[] encrypt(String data, Key key) {
        try {
            //取公钥
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(data.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     */
    public static byte[] decrypt(byte[] data, Key key) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
